package main

import "fmt"

// ==========接口==========
// 1、interface类型可以定义一组方法, 但是这些方法不需要实现. 并且interface不能包含任何变量
// 2、接口基本语法
// type 接口名 interface {
//	    method1(参数类别) 返回值类别
//	    method2(参数类别) 返回值类别
//	}
//	      实现接口 | 所有方法
//	              V
//	func (t 自定义类型) method1(参数类别) 返回值类别 {
//	    // 方法实现
//	}
//	func (t 自定义类型) method2(参数类别) 返回值类别 {
//	    // 方法实现
//	}
// 3、接口体现了程序设计的多态和高内聚低耦合的思想
// 4、接口的注意事项
// - 接口本身不能创建实例, 但是可以指向一个实现了该接口的自定义类型的变量
// - 接口中所有的方法都没有方法体, 即都是没有实现的方法
// - 在golang中, 一个自定义类型需要将某个接口的所有方法都实现, 则这个自定义类型实现了该接口
// - 一个自定义类型只要实现了该接口, 就能将自定义类型的实例(变量)赋给接口类型
// - 只要是自定义数据类型, 就可以实现接口, 不仅仅是结构体类型
// - 一个自定义类型可以实现多个接口
// - 一个接口(比如A接口)可以继承多个别的接口(比如B、C接口), 这时如果要实现A接口, 也必须将B、C接口的方法也全部实现
// - 空接口interface{}没有任何方法, 所以所有类型都实现了空接口, 可以把任何一个变量赋给空接口对象

// Usb接口
type Usb interface {
	Start()
	Stop()
}

type Phone struct{}

// 让Phone实现Usb接口的Start方法
func (p Phone) Start() {
	fmt.Println("手机开始工作")
}

// 让Phone实现Usb接口的Stop方法
func (p Phone) Stop() {
	fmt.Println("手机停止工作")
}

func (p Phone) Test() {}

type Camera struct{}

// 让Camera实现Usb接口的Start方法
func (c Camera) Start() {
	fmt.Println("相机开始工作")
}

// 让Camera实现Usb接口的Stop方法
func (c Camera) Stop() {
	fmt.Println("相机停止工作")
}

type Computer struct{}

// 编写一个Work方法, 接收一个Usb接口类型变量
// 只要变量实现了Usb接口声明的所有方法, 则该变量就可以作为Usb接口类型变量传给Computer
func (c Computer) Work(usb Usb) {
	usb.Start()
	usb.Stop()
}

type Integer int

func (i Integer) Start() {
	fmt.Println("Integer Start")
}

func (i Integer) Stop() {
	fmt.Println("Integer Stop")
}

type A interface {
	TestA()
}

type B interface {
	TestB()
}

type C interface {
	// 接口A和接口B不能有相同的方法
	A       // C接口继承了A接口
	B       // C接口继承了B接口
	TestC() // C接口自己的方法
}

type Multi struct{}

func (m Multi) TestA() {}

func (m Multi) TestB() {}

func (m Multi) TestC() {
	fmt.Println("Multi 实现了C接口")
}

func Test(c C) {
	c.TestC()
}

// 没有定义任何方法的接口就是空接口, 该接口变量可以接收任意数据类型
type EmptyInterface interface{}

type Stu struct{}

func (s *Stu) Start() {}

func (s *Stu) Stop() {}

func main() {
	computer := Computer{}
	phone := Phone{}
	camera := Camera{}
	computer.Work(phone)
	computer.Work(camera)

	// 接口本身不能创建实例, 但是可以指向一个实现了该接口的自定义类型的变量
	// 可以将实现了Usb接口的结构体变量phone赋值给Usb变量u, u调用Start相当于是phone调用了自身的Start方法
	var u Usb = phone
	u.Start() // 手机开始工作
	u.Stop()  // 手机停止工作

	// 只要是自定义数据类型, 就可以实现接口, 不仅仅是结构体类型
	u = Integer(1)
	u.Start() // Integer Start
	u.Stop()  // Integer Stop

	// 一个接口(比如A接口)可以继承多个别的接口(比如B、C接口), 这时如果要实现A接口, 也必须将B、C接口的方法也全部实现
	var m Multi
	Test(m) // Multi 实现了C接口

	// 空接口可以接收任意数据类型
	var E EmptyInterface = 1
	fmt.Println(E) // 1
	var e interface{} = true
	fmt.Println(e) // true

	// Stu指针类型变量实现了Usb接口, 所以不能把Stu变量赋值给Usb变量
	// var s Stu
	// 错误: var u Usb = s
	var s = &Stu{}
	u = s
	u.Start()

}
